﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Windows.Threading;
using LaunchMeNot.Core;
using LaunchMeNot.DAL;
using Lime49.UI;
using System.Threading;
using System.Diagnostics;

namespace LaunchMeNot
{
    /// <summary>
    /// A window which loads available entries, then starts launching
    /// </summary>
    public partial class WinLaunch : Window
    {
        private BackgroundWorker _worker;
        private ObservableCollection<PendingLaunchMeNotEntry> _entries;
        private int _entriesRemaining = 0;
        private readonly DispatcherTimer _timer;
        private bool _canAbort = true;

        public static readonly DependencyProperty IsAbortedProperty = DependencyProperty.Register("IsAborted", typeof(bool), typeof(WinLaunch));
        /// <summary>
        /// Determines whether the the launch process has been aborted.
        /// </summary>
        /// <value><c>true</c> if the launch process has been aborted, otherwise <c>false</c>.</value>
        public bool IsAborted
        {
            get { return (bool)GetValue(IsAbortedProperty); }
            set { SetValue(IsAbortedProperty, value); }
        }

        public WinLaunch()
        {
            _timer = new DispatcherTimer();
            _timer.Interval = TimeSpan.FromSeconds(1);
            _timer.Tick += new EventHandler(timer_Tick);
            InitializeComponent();
            countdownTimer.IsArmed = true;
            SetStatus("Preparing to launch applications, press any key to abort...");
            this.Topmost = WPFConfigurationManager.AutoLaunchAlwaysOnTop;
            if (!CoreConfigurationManager.AllowCancelAutoLaunch) {
                this._canAbort = false;
            }
        }

        /// <summary>
        /// Loads applications to launch and starts the timer when the Window loads.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _worker = new BackgroundWorker();
            _worker.WorkerSupportsCancellation = true;
            _worker.DoWork += (s, dwe) => dwe.Result = DALConfigurationManager.LaunchMeNotStore.GetStartupEntries().Cast<LaunchMeNotEntry>();
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                prgStatus.IsIndeterminate = false;
                prgStatus.Value = 0;
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, "Error Loading Entries");
                } else if (rwe.Result is IEnumerable<LaunchMeNotEntry>) {
                    _entries = new ObservableCollection<PendingLaunchMeNotEntry>((rwe.Result as IEnumerable<LaunchMeNotEntry>).Where(entry => entry.IsEnabled)
                                                                                                                              .Select(entry => new PendingLaunchMeNotEntry(entry))
                                                                                                                              .OrderBy(entry => entry.LaunchOrder));
                    _entriesRemaining = _entries.Count;
                    lstEntries.ItemsSource = _entries;
                    if (_entriesRemaining == 0) {
                        SetStatus("Nothing to launch");
                        txtCountdown.Visibility = Visibility.Collapsed;
                        CloseWindow(5);
                    } else {
                        countdownTimer.TimeRemaining = TimeSpan.FromSeconds(WPFConfigurationManager.AutoLaunchDelay);
                        _timer.Start();
                    }
                }
            };
            _worker.RunWorkerAsync();
            LaunchMeNot.Core.Utils.WaitForWorker(_worker);
            CommandManager.InvalidateRequerySuggested();
        }

        /// <summary>
        /// Prevents the Window from closing if the setting is enabled.
        /// </summary>
        /// <param name="e">A <see cref="T:System.ComponentModel.CancelEventArgs"/> that contains the event data.</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            if (!_canAbort) {
                base.OnClosing(e);
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Decrements the timer every second. If the timer is done, executes the launch command.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            if (IsAborted) {
                _timer.Stop();
                SetStatus("Aborted");
            } else if (countdownTimer.IsArmed) {
                if (countdownTimer.IsDone) {
                    Launch();
                    _timer.Stop();
                    this.Title = "Launching Applications";
                } else {
                    countdownTimer.TimeRemaining = countdownTimer.TimeRemaining.Subtract(TimeSpan.FromSeconds(1));
                    this.Title = string.Format("{0} until launch - LaunchMeNot", Lime49.Utils.PrettyTimeSpan(countdownTimer.TimeRemaining));
                }
            }
        }

        private void CanLaunch(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = IsLoaded && countdownTimer.TimeRemaining.TotalSeconds > 0;
        }

        /// <summary>
        /// Starts launching applications.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void Launch(object sender, ExecutedRoutedEventArgs e) {
            Launch();
        }


        /// <summary>
        /// Starts launching applications.
        /// </summary>
        private void Launch() {
            SetStatus("Launching applications...");
            txtCountdown.Visibility = Visibility.Collapsed;
            //txtCountdown.BeginAnimation(FrameworkElement.HeightProperty, new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(500))));
            _worker = new BackgroundWorker();
            _worker.WorkerReportsProgress = true;
            _worker.WorkerSupportsCancellation = true;
            var entriesToLaunch = _entries;
            int recheckDelay = 100;// the delay to wait before checking whether the required delay has been reached.
            _worker.ProgressChanged += (s, pce) =>
            {
                prgStatus.Value = pce.ProgressPercentage;
            };
            _worker.DoWork += (s, dwe) => LaunchMeNotBLL.AutoLaunch(entriesToLaunch, recheckDelay, _worker);
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(IsAborted ? "Aborted" : "Done");
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, "Error");
                }
                CloseWindow(5);
            };
            _worker.RunWorkerAsync();
        }

        private void CanAbort(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = IsLoaded && _canAbort && !IsAborted && _entriesRemaining > 0;
        }

        /// <summary>
        /// Sets the status message and aborts launching.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void Abort(object sender, ExecutedRoutedEventArgs e)
        {
            if (!_canAbort)
                return;
            SetStatus("Aborted");
            IsAborted = true;
            txtCountdown.Visibility = Visibility.Collapsed;
            this.Title = "LaunchMeNot";
            //txtCountdown.BeginAnimation(FrameworkElement.HeightProperty, new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(500))));
            if (_worker != null && _worker.IsBusy && _worker.WorkerSupportsCancellation) {
                _worker.CancelAsync();
                if (_worker.WorkerReportsProgress) {
                    _worker.ReportProgress(100, null);
                }
            }
            CloseWindow(5);
        }

        /// <summary>
        /// Closes the window after a delay.
        /// </summary>
        /// <param name="delay">The number of seconds to wait before closing.</param>
        private void CloseWindow(int delay)
        {
            _canAbort = true;
            LaunchMeNotCommands.Abort.Execute(null, this);
            Title = "LaunchMeNot";
            LaunchMeNot.Core.Utils.WaitForWorker(_worker);
            if (WPFConfigurationManager.CloseAfterAutoLaunch) {
                _worker = new BackgroundWorker();
                _worker.WorkerSupportsCancellation = true;
                _worker.DoWork += (s, dwe) => Thread.Sleep(delay*1000);
                _worker.RunWorkerCompleted += (s, rwe) => Close();
                _worker.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Sets the status text.
        /// </summary>
        /// <param name="message">The message.</param>
        private void SetStatus(string message)
        {
            lblStatus.Text = message;
        }

        /// <summary>
        /// Listens for keypresses to abort launching.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        private void Window_KeyDown(object sender, KeyEventArgs e) {
            LaunchMeNotCommands.Abort.Execute(null, this);
        }

        /// <summary>
        /// Shows the 'About LaunchMeNot' window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            WinAbout dlg = new WinAbout() { Topmost = true };
            dlg.ShowDialog();
        }
    }
}
